/*
 *  PillCage.m
 *  pogostick
 *
 *  Created by daddy (chuck@kollar.com) on 10/8/09.
 *  Copyright 2009 Charles P. Kollar All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#import "PillCage.h"


extern NSMutableArray *bulletsToDelete;

@implementation PillCage

@synthesize space;
@synthesize identifier;
@synthesize doorHinge;
@synthesize pills;
@synthesize facingLeft;
@synthesize triggerShape;
@synthesize collisionType;
@synthesize hasBeenOpened;

// This should be a function of the area of the cage and not a constant
#define MAX_PILLS_TO_MAKE               6
#define MIN_PILL_MULTIPLIER         0.06f
#define MAX_PILL_ADDER              0.03f


// after these pills are out ofthe box, impart more 'e' to the shape and keep them
// moving so that the stick has to catch them....
// NOTE: if you change the thinkness of a line you will also have to make the pills
// a different size. If the line thickness is decreased at some size the pills will
// simply fall through the floor. If the line thickness is made smaller then the
// pills will need to be made bigger...
-(cpShape *)makePillSize:(cpVect)size pos:(cpVect)pos
{
	cpFloat radius = (rand() % (int)(size.x*MAX_PILL_ADDER)) + size.x*MIN_PILL_MULTIPLIER;
	cpBody *pillBody = cpBodyNew(1.0f, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero));
	pillBody->p = pos;
	cpSpaceAddBody(space, pillBody);
    
	cpShape *shape = cpCircleShapeNew(pillBody, radius, cpvzero);
    // the 'e' needs to be 1.1 to start bouncing in groups, much above that and it is too active.
	shape->e = 1.05f; shape->u = 0.0f;
    // This pill moves so it is not attached as a static shape...
	shape->collision_type = kColl_Pill2;
    // so that the function removeShapeWrapperForShapeInPillCage can enumerate over the pills
    // associated with the cage and delete it if necessary.
    shape->data = self;
	cpSpaceAddShape(space, shape);  
	float x = rand() % 40;
	float y = rand() % 40;
	x -= 20.0f;
	y -= 20.0f;
	cpBodyApplyImpulse(pillBody, cpv(x, y), cpv(10.0f,20.0f));
    
    return shape;
}

// defined in floor.m
extern cpFloat getLowestFloorY();

-(void) performActions
{
    // Remove any pills that have fallen off of the world....
    // Even though they are still bouncing around and out of the cage
    // we still keep track of them through their association with this cage.
    for (int i = [pills count]-1; i >= 0; i--) {
        ShapeWrapper *shapeWrapper = [pills objectAtIndex:i];
        if ([shapeWrapper shape]->body->p.y < getLowestFloorY()) {
            [bulletsToDelete addObject: shapeWrapper];
            [pills removeObjectAtIndex:i];
        }
    }
    
    // Here we give the pills a little bit of random motion....
    NSEnumerator *enumerator = [pills objectEnumerator];
    id element;
    while (element = [enumerator nextObject]) {
        ShapeWrapper *shapeWrapper = (ShapeWrapper *)element;
        // compute a new force driving it's position...
        float x = rand() % 40;
        float y = rand() % 40;
        x -= 20.0f;
        y -= 20.0f;
        cpBodyApplyImpulse([shapeWrapper shape]->body, cpv(x, y), cpv(10.0f,20.0f));
    }
}

-(id)initInSpace:(cpSpace *)sp toStaticBody:(cpBody *)staticBody identifier:(NSString *)ident collisionType:(cpCollisionType)ct facingLeft:(BOOL)fl atPosition:(cpVect)p size:(cpVect)sz
{
    if (self = [super init]) {
        self.space = sp;
        self.identifier = [[NSString alloc] initWithString:ident];
        self.collisionType = ct;
        self.facingLeft = fl;
        self.hasBeenOpened = NO;
        cpFloat doorMass = 10.0f;
        cpFloat wall_width = 8.0f;
        cpFloat door_height = sz.y*0.6f;
        cpFloat box_width = sz.x*0.6f;

        cpVect doorCenter = cpvadd(p, cpv((fl == YES ? 1 : -1) * (box_width + wall_width), 0.0f));
        cpVect verts[] = {
            cpv(-wall_width,-door_height),
            cpv(-wall_width,door_height),
            cpv(wall_width,door_height),
            cpv(wall_width,-door_height)
        };
        cpBody *doorBody = cpBodyNew(doorMass, cpMomentForPoly(doorMass, (sizeof(verts)/sizeof(cpVect)), verts, cpvzero));
        doorBody->p = doorCenter;
        cpSpaceAddBody(space, doorBody);
        cpShape *shape = cpPolyShapeNew(doorBody, (sizeof(verts)/sizeof(cpVect)), verts, cpvzero);
        shape->e = 0.0f; shape->u = 1.0f;
        // when the door swings open the pogostick should be able to jump on it...
        shape->collision_type = ct;
        cpSpaceAddShape(space, shape);
        
        // possibly better would be a gear joint that raises when the user presses a button...
        // The pin joint is the attachment, and the gear what moves it...
        cpConstraint *joint = cpPinJointNew(staticBody, doorBody, cpvadd(doorBody->p, cpv(0.0f, door_height)), cpv(0.0f, door_height));
        cpSpaceAddConstraint(space, joint);
        joint = cpGearJointNew(staticBody, doorBody, 0.0f, 1.0f);
        cpSpaceAddConstraint(space, joint);
        
        cpVect topCenter = cpvadd(p, cpv(0.0f, door_height+wall_width));
        cpVect verts2[] = {
            cpv(topCenter.x-box_width,topCenter.y-wall_width),
            cpv(topCenter.x-box_width,topCenter.y+wall_width),
            cpv(topCenter.x+box_width,topCenter.y+wall_width),
            cpv(topCenter.x+box_width,topCenter.y-wall_width)
        };
        shape = cpPolyShapeNew(staticBody, (sizeof(verts2)/sizeof(cpVect)), verts2, cpvzero);
        shape->e = 0.0f; shape->u = 1.0f;
        // only the top has a collision type...
        shape->collision_type = ct;
        // index this instance by the shape that will trigger the execution of the 'cpSpaceAddCollisionPairFunc'.
        triggerShape = shape;
        cpSpaceAddShape(space, shape);

        cpVect wRCtr = cpvadd(p, cpv((fl == YES ? -1 : 1) * box_width, door_height/2));
        shape = cpSegmentShapeNew(staticBody, cpv(wRCtr.x,wRCtr.y-door_height/2), cpv(wRCtr.x,wRCtr.y+door_height/2), 0.0f);
        shape->e = 0.0f; shape->u = 1.0f;
        cpSpaceAddShape(space, shape);
        
        cpVect bottomA = cpvadd(doorCenter, cpv(wall_width,-door_height));
        cpVect bottomB = cpv(wRCtr.x,wRCtr.y-door_height/2);
        shape = cpSegmentShapeNew(staticBody, (fl == YES ? bottomB : bottomA), (fl == YES ? bottomA : bottomB), 0.0f);
        shape->e = 0.0f; shape->u = 1.0f;
        cpSpaceAddShape(space, shape);
       
        // make the little pills to fill the space....
        pills = [[NSMutableArray alloc] initWithCapacity: MAX_PILLS_TO_MAKE];
        
        cpFloat maxPillArea = sz.x*MAX_PILL_ADDER * sz.x*MIN_PILL_MULTIPLIER * 8;
        cpFloat maxCageArea = sz.x * sz.y/2;
        int pillsToMake = (int)(maxCageArea / maxPillArea);
        pillsToMake = (pillsToMake > MAX_PILLS_TO_MAKE) ? MAX_PILLS_TO_MAKE : pillsToMake;
        for (int i=0; i<pillsToMake; i++) {
            cpShape *pill = [self makePillSize:sz pos:cpv(p.x, p.y+door_height/4)];
            // add a pointer to this cage instance so that we can delete the pill from the pills array
            // when the pill is removed from the game see the cp call back function "pill2HitStick"
            // found in "GameLayer.m". see the function 'removeShapeWrapperForShapeInPillCage' above.
            pill->data = self;
            ShapeWrapper *sw = [[ShapeWrapper alloc] initWithShape:pill space:space isStaticShape:NO];
            [pills addObject: sw];
            [sw release];
        }
        doorHinge = joint;
    }
	return self;    
}

-(void) dealloc
{
    [identifier release];
    [pills release];

    [super dealloc];
}

@end
